package com.woniuxy.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.woniuxy.entity.AllUser;
import com.woniuxy.entity.ArticleLike;
import com.woniuxy.entity.DTO.algorithm.UserChurnDTO;
import com.woniuxy.entity.DTO.UserQueryDTO;
import com.woniuxy.mapper.ArticleLikesMapper;
import com.woniuxy.mapper.UserMapper;
import com.woniuxy.service.IUserChurnService;
import com.woniuxy.service.IUserService;
import com.woniuxy.util.PageResult;
import com.woniuxy.util.ResponseData;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.*;
import java.util.stream.Collectors;

/**
 * @Author 陈华庆
 * @Date 2025/7/23 4:12
 * @Description TODO
 * 用户流失预警服务实现
 * 采用活跃度衰减模型：
 * 1. 基于用户历史点赞行为计算活跃频率
 * 2. 根据最后活跃时间与当前时间间隔评估风险
 * 3. 结合用户注册时长调整风险权重
 */
@Service
public class UserChurnServiceImpl implements IUserChurnService {

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private ArticleLikesMapper articleLikesMapper;

    @Autowired
    private IUserService userService;

    // 风险评估阈值配置
    private static final int HIGH_RISK_DAYS = 30; // 高风险：超过30天未活跃
    private static final int MEDIUM_RISK_DAYS = 15; // 中风险：超过15天未活跃
    private static final int LOW_RISK_DAYS = 7; // 低风险：超过7天未活跃

    // 历史数据窗口（天）
    private static final int HISTORY_WINDOW = 90;

    @Override
    public ResponseData<PageResult<UserChurnDTO>> getHighRiskUsers(Integer pageNum, Integer pageSize) {
        // 1. 获取所有用户基础信息
        UserQueryDTO queryDTO = new UserQueryDTO();
        queryDTO.setPageNum(pageNum);
        queryDTO.setPageSize(pageSize);
        // 将queryUsers修改为getUserList，匹配IUserService中实际存在的方法
        PageResult<AllUser> userPage = userService.getUserList(queryDTO);

        // 2. 批量评估用户流失风险
        List<UserChurnDTO> churnList = userPage.getList().stream()
                .map(this::calculateUserChurnRisk)
                .filter(dto -> "HIGH".equals(dto.getRiskLevel()) || "MEDIUM".equals(dto.getRiskLevel()))
                .collect(Collectors.toList());

        // 3. 构建分页结果
        PageResult<UserChurnDTO> result = new PageResult<>();
        result.setPageNum(pageNum);
        result.setPageSize(pageSize);
        result.setTotal((long) churnList.size());
        result.setPages((int) Math.ceil((double) churnList.size() / pageSize));
        result.setList(churnList);

        return ResponseData.ok(result);
    }

    @Override
    public ResponseData<UserChurnDTO> evaluateUserChurnRisk(Long userId) {
        // 1. 获取用户基本信息
        AllUser user = userMapper.selectById(userId);
        if (user == null) {
            return ResponseData.fail(404, "用户不存在");
        }

        // 2. 计算流失风险
        UserChurnDTO churnDTO = calculateUserChurnRisk(user);
        return ResponseData.ok(churnDTO);
    }

    /**
     * 计算单个用户的流失风险
     */
    private UserChurnDTO calculateUserChurnRisk(AllUser user) {
        UserChurnDTO dto = new UserChurnDTO();
        dto.setUserId(user.getUserId());
        dto.setUsername(user.getUsername());

        // 1. 获取用户最后活跃时间（最近点赞时间）
        Date lastActiveTime = getLastActiveTime(user.getUserId());
        dto.setLastActiveTime(lastActiveTime);

        // 2. 计算未活跃天数
        int inactiveDays = calculateInactiveDays(lastActiveTime);
        dto.setInactiveDays(inactiveDays);

        // 3. 计算历史活跃频率
        double avgActiveFrequency = calculateAverageActiveFrequency(user.getUserId());
        dto.setAvgActiveFrequency(avgActiveFrequency);

        // 4. 评估风险等级和分数
        evaluateRiskLevel(dto, inactiveDays, avgActiveFrequency);

        return dto;
    }

    /**
     * 获取用户最后活跃时间（最近点赞时间）
     */
    private Date getLastActiveTime(Long userId) {
        QueryWrapper<ArticleLike> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id", userId)
                .eq("status", 1) // 只考虑有效点赞
                .orderByDesc("create_time")
                .last("LIMIT 1");

        ArticleLike latestLike = articleLikesMapper.selectOne(queryWrapper);
        return latestLike != null ? latestLike.getCreateTime() : null;
    }

    /**
     * 计算未活跃天数
     */
    private int calculateInactiveDays(Date lastActiveTime) {
        if (lastActiveTime == null) {
            return Integer.MAX_VALUE; // 从未活跃
        }

        long now = new Date().getTime();
        long lastActive = lastActiveTime.getTime();
        return (int) ((now - lastActive) / (1000 * 60 * 60 * 24));
    }

    /**
     * 计算平均活跃频率（天/次）
     */
    private double calculateAverageActiveFrequency(Long userId) {
        Calendar calendar = Calendar.getInstance();
        calendar.add(Calendar.DAY_OF_YEAR, -HISTORY_WINDOW);
        Date startTime = calendar.getTime();

        QueryWrapper<ArticleLike> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("user_id", userId)
                .eq("status", 1)
                .ge("create_time", startTime);

        List<ArticleLike> likes = articleLikesMapper.selectList(queryWrapper);
        if (likes.size() < 2) {
            return Double.MAX_VALUE; // 活跃次数不足，无法计算频率
        }

        // 按时间排序
        likes.sort(Comparator.comparing(ArticleLike::getCreateTime));

        // 计算相邻点赞时间间隔的平均值
        long totalInterval = 0;
        for (int i = 1; i < likes.size(); i++) {
            long interval = likes.get(i).getCreateTime().getTime() - likes.get(i-1).getCreateTime().getTime();
            totalInterval += interval / (1000 * 60 * 60 * 24); // 转换为天
        }

        return (double) totalInterval / (likes.size() - 1);
    }

    /**
     * 评估风险等级和分数
     */
    private void evaluateRiskLevel(UserChurnDTO dto, int inactiveDays, double avgActiveFrequency) {
        // 从未活跃用户直接判定为高风险
        if (dto.getLastActiveTime() == null) {
            dto.setRiskLevel("HIGH");
            dto.setRiskScore(95.0);
            return;
        }

        // 基于未活跃天数和历史活跃频率计算风险分数
        double baseScore = Math.min(100, inactiveDays * 2.5);
        double frequencyFactor = avgActiveFrequency > 0 ? Math.min(2, inactiveDays / avgActiveFrequency) : 1;
        double riskScore = baseScore * frequencyFactor;

        // 确定风险等级
        if (inactiveDays >= HIGH_RISK_DAYS || riskScore >= 70) {
            dto.setRiskLevel("HIGH");
        } else if (inactiveDays >= MEDIUM_RISK_DAYS || riskScore >= 40) {
            dto.setRiskLevel("MEDIUM");
        } else if (inactiveDays >= LOW_RISK_DAYS || riskScore >= 20) {
            dto.setRiskLevel("LOW");
        } else {
            dto.setRiskLevel("STABLE");
        }

        dto.setRiskScore(Math.min(100, riskScore));
    }
}
